package com.moyq5.wifi.joystick.command;

/**
 * 标准指令抽象
 * @author mo_yq5
 * @since 2021/11/25
 */
public abstract class CommandAbstract implements Command {
    @Override
    public final byte[] bytes() {
        byte[] contents = content();
        byte[] bytes = new byte[10 + contents.length];
        // 包结构：以固定0xFBFB 开头 2字节
        bytes[0] = (byte) 0xFB;
        bytes[1] = (byte) 0xFB;
        // 包结构：整个数据包长度，2字节，高位在前，最大值65535，约为65K
        bytes[2] = (byte)(( bytes.length >> 8) & 0xFF);
        bytes[3] = (byte)(( bytes.length) & 0xFF);
        // 包结构：指令类型，1字节，第8位是响应类型，前7位是指令类型，最多支持127种指令
        bytes[4] |= (byte)(getAckType() & 0x80);
        bytes[4] |= (byte)(getCmdType() & 0x7F);
        // 包结构：指令序列号，2字节，高位在前，最大值65535
        int serialNo = getSerialNo();
        bytes[5] = (byte)(( serialNo >> 8 ) & 0xFF);
        bytes[6] = (byte)(serialNo & 0xFF);
        // 包结构：业务内容，自定义长度
        System.arraycopy(contents, 0, bytes, 7, contents.length);
        // 包结构：倒数第3字节为检验值，1字节，校验内容为长度值到校验值之前的内容
        bytes[bytes.length - 3] = (byte) (crc(bytes) & 0xFF);
        // 包结构：以固定0xEDED 结尾 2字节
        bytes[bytes.length - 2] = (byte) 0xED;
        bytes[bytes.length - 1] = (byte) 0xED;
        return bytes;
    }

    /**
     * 指令请求/响应类型
     * @return 指令收发类型: 0发送，1响应
     */
    protected abstract int getAckType();
    /**
     * 指令类型
     * @return 指令类型
     */
    protected abstract int getCmdType();
    /**
     * 指令序列号，建议每次发送同一类指令时序列号递增，响应时原样回传。
     * @return 指令序列号
     */
    protected abstract int getSerialNo();

    protected abstract byte[] content();

    public abstract String format();

    private static int crc(byte[] bytes) {
        int crc = 0;
        // 校验内容为长度值到校验值之前的内容
        for (int i = 2; i < bytes.length - 3; i++) {
            crc ^= bytes[i];
        }
        return crc;
    }


}
